<!-- 文件上传，基于 ElUpload 二次封装 -->
<script lang="ts" setup>
import type {
  UploadFile,
  UploadInstance,
  UploadProps,
  UploadRawFile,
  UploadRequestOptions,
  UploadUserFile,
} from 'element-plus';
import { genFileId } from 'element-plus';

import type { components } from '@/genapi/wh';

import { formatFilePath, uploadRequest } from './upload-request';

type SystemFileDuiXiang = components['schemas']['SystemFileDuiXiang'];

export interface FileUploadProps {
  modelValue?: SystemFileDuiXiang[];
  limit?: number;
  [x: string]: any;
}

export interface FileUploadEmits {
  (event: 'update:modelValue', data?: SystemFileDuiXiang[]): void;
}

defineOptions({ name: 'FileUpload', extends: ElUpload });
const props = defineProps<FileUploadProps>();
const emit = defineEmits<FileUploadEmits>();

// id列表
const fileIds = computed(
  () => (props.modelValue?.map((e) => e?.id)?.filter((e) => !!e) as string[]) ?? [],
);

const fileList = ref<UploadUserFile[]>([]);

watchDeep(fileList, (fileList) => {
  emit(
    'update:modelValue',
    fileList
      .filter((e: any) => e.status === 'success' && e.response?.id)
      .map((e) => e.response as any),
  );
});

watchArray(
  fileIds,
  (_value, _prev, added, removed) => {
    added.forEach((id) => {
      const index = fileList.value.findIndex((e: any) => e.response?.id === id);
      if (index === -1) {
        const response = props.modelValue?.find((e) => e.id === id);
        response &&
          fileList.value.push({
            name: response.name ?? '',
            url: formatFilePath(response),
            size: response.size,
            percentage: 1,
            status: 'success',
            response,
          });
      }
    });
    removed?.forEach((id) => {
      const index = fileList.value.findIndex((e: any) => e.response?.id === id);
      if (index !== -1) {
        fileList.value.splice(index, 1);
      }
    });
  },
  {
    flush: 'post',
    immediate: true,
  },
);

const uploadRef = shallowRef<UploadInstance>();

const handleExceed: UploadProps['onExceed'] = (files: any) => {
  if (props.limit === 1) {
    uploadRef.value?.clearFiles?.();
    const file = files[0] as UploadRawFile;
    file.uid = genFileId();
    uploadRef.value?.handleStart(file);
    uploadRef.value?.submit();
  }
};

async function httpRequest(options: UploadRequestOptions): Promise<any> {
  const file = options.file as unknown as File & { uid?: string };
  try {
    const { data } = await uploadRequest(file, (evt) =>
      options.onProgress({ ...evt, percent: evt.total / evt.loaded }),
    );
    options.onSuccess(data);
    return data;
  } catch (error: any) {
    return options.onError(error);
  }
}

defineExpose({
  handleRemove: (file: UploadFile) => uploadRef.value?.handleRemove(file),
  handleStart: (rawFile: UploadRawFile) => uploadRef.value?.handleStart(rawFile),
  clearFiles: (status: any) => uploadRef.value?.clearFiles(status),
  submit: () => uploadRef.value?.submit(),
  abort: (file: UploadFile) => uploadRef.value?.abort(file),
  // 判断是否没有正在上传的
  isFinish() {
    return !fileList.value?.find((e) => e.status === 'uploading');
  },
});

const vm = getCurrentInstance();

const attrs = computed(() => {
  return {
    ...vm?.vnode?.props,
    ...props,
    onChange: undefined,
    onRemove: undefined,
  } as any;
});

async function change() {
  await nextTick();
  triggerRef(fileList);
}
</script>

<template>
  <ElUpload
    v-bind="attrs"
    ref="uploadRef"
    v-model:file-list="fileList"
    class="file-upload"
    auto-upload
    :http-request="httpRequest"
    :on-exceed="handleExceed"
    :on-change="change"
    :on-remove="change"
  >
    <template v-for="(_item, key) in $slots" #[key]="data">
      <slot v-bind="data" :name="key" />
    </template>
    <template v-if="!$slots['defualt']">
      <el-icon class="i-ep:plus" text="#D9D9D9! 30px!" />
    </template>
  </ElUpload>
</template>

<style lang="scss">
.file-upload {
  .el-upload-list--picture-card {
    .el-upload-list__item,
    .el-upload--picture-card {
      width: 104px;
      height: 90px;
    }

    &.is-disabled {
      .el-upload--picture-card {
        display: none;
      }
    }
  }
}
</style>
